
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sstream>
#include "MemCache.h"
#include "define.h"
#include "Debug.h"
#include "Helper.h"
#include "dlfacade.h"
 
using namespace std;
Helper * Helper::_instance = NULL;

Helper::Helper() 
{
	m_pCache = NULL;
	m_pStorage = NULL;
}

Helper::~Helper()
{
	if( m_pCache != NULL)
		delete m_pCache;
 
        DlFacade::Release(true);
        m_pStorage = NULL;
}

Helper * Helper::Instance(const char *confFile)
{
	if (NULL == _instance) 
	{
		_instance = new Helper();
		if ( !_instance->Startup(confFile) ) {
			delete _instance; 
			_instance = NULL;
		}
	}
	return _instance;
}

void Helper::Release(bool bForce)
{
	if (_instance) 
	{
		if(bForce)
		{
			delete _instance; 
			_instance = NULL;
		}
	}
}

bool  Helper::Startup(const char *confFile)
{
	if(confFile != NULL)
		m_sConfFile  = confFile;
	if(m_sConfFile.length() < 1)
	{
		ERRO<<": no helper.conf be given!"<<endl;
		return false;
	}
	struct stat st;
	if (lstat(confFile, &st) != 0)
	{
		ERRO<<":"<<confFile<<" is not exist!"<<endl;
		return false;
	}

	GKeyFile *kv = g_key_file_new();
	GError *err = NULL;
	if(TRUE != g_key_file_load_from_file(kv,confFile, G_KEY_FILE_NONE , &err))
	{
		if (err != NULL)
		{
			ERRO << "Unable to read file:" << err->message << endl;
			g_error_free (err);
		}
		g_key_file_free(kv);
		return false;
	}

	char *pval =  g_key_file_get_value(kv,"global", "ip_acl_config",  NULL);
	if(pval == NULL)
        { 
                m_sIPAcl = "";
		ERRO <<": no ip_acl_config setting "<< endl;
        } 
	else
	{

		m_sIPAcl  = pval;
                g_free(pval);
	}

	pval =  g_key_file_get_value(kv,"global", "cache_config",  NULL);
	if(pval == NULL)
		ERRO <<": no cache_config setting "<< endl;
	else
	{

		m_sCache_Cfg  = pval;
                g_free(pval);
	}

	pval =  g_key_file_get_value(kv,"global", "storage_config",  NULL);
	if(pval == NULL)
		ERRO <<":no storage_config setting "<< endl;
	else
	{

		m_sStorage_Cfg  = pval;
		g_free(pval);
	}

	pval =  g_key_file_get_value(kv,"global", "storage",  NULL);
	if(pval == NULL)
		ERRO <<":no storage setting "<< endl;
	else
	{
		
                DlFacade *dlp = DlFacade::Instance((char *)pval, m_sStorage_Cfg.c_str()); 
                if(dlp != NULL)
                {
                     m_pStorage = dlp->GetStorage();
                }
		g_free(pval);
	}

	pval =  g_key_file_get_value(kv,"global", "port",  NULL);
	if(pval == NULL)
		m_iPort = 443;
	else
	{
		m_iPort = atoi((char *) pval);
		g_free(pval);
	}

	pval =  g_key_file_get_value(kv,"global", "wport",  NULL);
	if(pval == NULL)
		m_iwPort = 9443;
	else
	{
		m_iwPort = atoi((char *) pval);
		g_free(pval);
	}

	pval =  g_key_file_get_value(kv,"global", "threads",  NULL);
	if(pval == NULL)
		m_iThreads = 10;
	else
	{
		m_iThreads = atoi((char *) pval);
		g_free(pval);
	}

        g_key_file_free(kv);
        DEBUG1(" MemCache ");
	m_pCache = new MemCache();
	bool bret = m_pCache->Initialize(m_sCache_Cfg.c_str());
	if(!bret)
	{
		ERRO <<"return = false: can't Initialize Cache Instance"<<endl;
		delete m_pCache;
		m_pCache = NULL;
		return false;
	}
	return true;
}

int Helper :: ResetCache(const char *ip, int port)
{
        DEBUG3(ip, " : ", port);
        return m_pCache->ResetCacheStatus(ip, port);
}

int Helper :: get_mem_return_info(char **wbuf, int retcode,unsigned &wsize, unsigned &wbytes)
{
	wbytes = 2*sizeof(int);
	DEBUG4("wsize=", wsize, "; wbytes=", wbytes );
	if(wsize < wbytes)
	{
DEBUG1("realloc(*wbuf, wbytes+1)");
		*wbuf = (char *)g_realloc(*wbuf, wbytes +1);
		if(*wbuf == NULL){
			ERRO<<"can't malloc for write buffer !"<<endl;
			wsize = wbytes = 0;
			return PANDA_ERR_SYSTEM;
		}
		wsize = wbytes + 1;
	}

        unsigned usedlen = 0;
DEBUG4("wbuf =", *wbuf,"; bcopy wbytes = ", wbytes);
        bcopy(&wbytes, *wbuf + usedlen, sizeof(unsigned int));            //str_length
        usedlen += sizeof(unsigned int);
DEBUG2("bcopy retcode = ", retcode);
        bcopy(&retcode, *wbuf + usedlen, sizeof(int));   // retcode
        
        return 0;
}

int Helper::write(const char *data, unsigned dlen)
{
    int iret = m_pCache->Set(data, dlen);
    if(m_pStorage != NULL)
         m_pStorage->Write(0, NULL);
    return iret;	
}

int Helper::read(const char *data, unsigned dlen, char **wbuf,unsigned &wsize, unsigned &wbytes)
{
   int iret = m_pCache->Get(data, dlen, wbuf, wsize, wbytes);
    if(m_pStorage != NULL)
         m_pStorage->Read(0, NULL, wbuf, wsize, wbytes);
    return iret;	
}

int Helper::incr(const char *data, unsigned dlen, char **wbuf,unsigned &wsize, unsigned &wbytes)
{
   int iret = m_pCache->Incr(data, dlen, wbuf, wsize, wbytes);
    if(m_pStorage != NULL)
         m_pStorage->Atom(0, NULL, wbuf, wsize, wbytes);
    return iret;	
}

int Helper:: decr(const char *data, unsigned dlen, char **wbuf,unsigned &wsize, unsigned &wbytes)
{
   int iret = m_pCache->Decr(data, dlen, wbuf, wsize, wbytes);
    if(m_pStorage != NULL)
         m_pStorage->Atom(0, NULL, wbuf, wsize, wbytes);
    return iret;	
}

int Helper:: remove(const char *data, unsigned dlen)
{
   int iret = m_pCache->Delete(data, dlen);
    if(m_pStorage != NULL)
         m_pStorage->Delete(0, NULL);
    return iret;	
}

int Helper :: Do_Method(int method, char *data, unsigned dlen,char **wbuf, unsigned &wsize, unsigned &wbytes)
{
        wbytes = 0;
	int iret = -1;
DEBUG6("wbuf =", *wbuf, "; wsize=", wsize,"; wbytes=", wbytes );
	switch(method)
	{ 
		case PANDA_MC_SET:
			return m_pCache->Set(data, dlen);
		case PANDA_MC_GET:
			get_mem_return_info(wbuf, iret , wsize, wbytes);
			iret = m_pCache->Get(data, dlen, wbuf, wsize, wbytes);
			if(iret != 0)
				get_mem_return_info(wbuf, iret , wsize, wbytes);
			break;
		case PANDA_MC_INCR:
			iret = m_pCache->Incr(data, dlen, wbuf, wsize, wbytes);
			if(iret != 0)
				get_mem_return_info(wbuf, iret , wsize, wbytes);
			break;
		case PANDA_MC_DECR: 
			iret= m_pCache->Decr(data, dlen, wbuf, wsize, wbytes);
			if(iret != 0)
				get_mem_return_info(wbuf, iret , wsize, wbytes);
			break;
		case PANDA_MC_DEL:
			return m_pCache->Delete(data, dlen );
		case PANDA_MC_DB_SET:
			return write(data, dlen);
		case PANDA_MC_DB_GET:
			iret = read(data, dlen, wbuf, wsize, wbytes);
			if(iret != 0)
				get_mem_return_info(wbuf, iret , wsize, wbytes);
			break;
		case PANDA_MC_DB_INCR:
			iret = incr(data, dlen, wbuf, wsize, wbytes);
			if(iret != 0)
				get_mem_return_info(wbuf, iret , wsize, wbytes);
			break;
		case PANDA_MC_DB_DECR: 
			iret= decr(data, dlen, wbuf, wsize, wbytes);
			if(iret != 0)
				get_mem_return_info(wbuf, iret , wsize, wbytes);
			break;
		case PANDA_MC_DB_DEL:
			return remove(data, dlen );
		default: return PANDA_ERR_SYSTEM;
	}
	DEBUG6("wbuf =", *wbuf, "; wsize=", wsize,"; wbytes=", wbytes );
	return iret;
}


